package com.zzs.client.service.impl;

import cn.hutool.core.util.RandomUtil;
import com.zzs.client.dao.mysql.UserMapper;
import com.zzs.client.entity.mysql.UserDO;
import com.zzs.client.service.UserService;
import com.zzs.common.core.constant.RedisKeyConsts;
import com.zzs.common.core.enums.ResponseCode;
import com.zzs.common.core.enums.UserRole;
import com.zzs.common.core.util.SnowflakeUtil;
import com.zzs.common.redis.service.RedisService;
import com.zzs.common.security.exception.ServiceException;
import com.zzs.common.security.service.TokenService;
import com.zzs.common.security.util.PasswordUtil;
import com.zzs.common.sms.service.SmsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.concurrent.TimeUnit;

/**
 * @author zzs
 * @date 2025/03/24
 */
@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserMapper userMapper;

    @Autowired
    private TokenService tokenService;

    @Autowired
    private SmsService smsService;

    @Autowired
    private RedisService redisService;

    // 每个手机号当日短信发送总次数
    @Value("${sms.aliyun.smsLimits}")
    private Integer smsLimits;


    @Override
    public String passwordLogin(String phone, String password) {
        UserDO userDO = userMapper.getIdAndPassword(phone);
        if (userDO == null) {
            throw new ServiceException(ResponseCode.USER_NO_EXISTS);
        }
        if (userDO.getPassword() == null || userDO.getPassword().isBlank()) {
            throw new ServiceException(ResponseCode.FAIL, "你还未设置密码, 请使用验证码登录");
        }
        if (!PasswordUtil.matchesPassword(password, userDO.getPassword())) {
            throw new ServiceException(ResponseCode.INVALID_CREDENTIALS);
        }
        return tokenService.createToken(userDO.getId(), UserRole.ORDINARY);
    }

    @Override
    public void logout(long userId) {
        tokenService.removeCache(userId);
    }

    @Override
    public void sendCode(String phone) {
        String smsCodeKey = RedisKeyConsts.getSmsCodeKey(phone);
//        判断redis中是否有存验证码
        if (redisService.hasKey(smsCodeKey)) {
            throw new ServiceException(ResponseCode.FAIL, "验证码已发送, 请勿频繁操作");
        }
//        判断这个用户当天总计获取的验证码次数
        String smsCountKey = RedisKeyConsts.getSmsCountKey(phone);
        Integer smsCount = redisService.getValue(smsCountKey, Integer.class);
        if (smsCount != null && smsCount >= smsLimits) {
            throw new ServiceException(ResponseCode.FAIL, "当日验证码已达到最大次数");
        }
//        生成6位随机验证码
        String randomNum = RandomUtil.randomNumbers(6);
        redisService.saveValue(smsCodeKey, randomNum, RedisKeyConsts.SMS_CODE_EXPIRE_TIME, RedisKeyConsts.SMS_CODE_TIME_UNIT);
        if (smsCount == null) {
//            说明是第一次发短信, 设置过期时间为第二天凌晨
            long expire = ChronoUnit.SECONDS.between(LocalDateTime.now(),
                    LocalDateTime.now().plusDays(1).withHour(0).withMinute(0).withSecond(0).withNano(0));
            redisService.saveValue(smsCountKey, 1, expire, TimeUnit.SECONDS);
        } else {
            redisService.increment(smsCountKey);
        }
//        发送验证码
        smsService.sendSmsCode(phone, randomNum);
    }

    @Override
    public String smsCodeLogin(String phone, String smsCode) {
        String smsCodeKey = RedisKeyConsts.getSmsCodeKey(phone);
        if (!redisService.hasKey(smsCodeKey)) {
            throw new ServiceException(ResponseCode.NOT_FOUND, "请先获取验证码");
        }
//        存储在redis中的正确的验证码
        String rawSmsCode = redisService.getValue(smsCodeKey, String.class);
        if (!rawSmsCode.equals(smsCode)) {
            throw new ServiceException(ResponseCode.INVALID_CREDENTIALS, "验证码错误");
        }
        Long userId = userMapper.getIdByPhone(phone);
        if (userId == null) {
//            用户是第一次登录, 也就是说，这个是注册请求
            userId = SnowflakeUtil.nextId();
            LocalDateTime now = LocalDateTime.now();
            UserDO userDO = UserDO.builder()
                    .id(userId)
                    .phone(phone)
                    .createTime(now)
                    .updateTime(now)
                    .build();
            userMapper.save(userDO);
        }
        redisService.removeKey(smsCodeKey);
        return tokenService.createToken(userId, UserRole.ORDINARY);
    }


}
